package puzzleEdit;

import java.util.Random;

import model.MatrixCube;

/**
 * @author      Zalan Czirjak <zaorsz@yahoo.com>
 * @version     2011.0509
 * @since       1.6
 */
public class CubeSlicer {
	/**A kocka amit majd szetszeletelunk. -3dmatrix;*/
	private MatrixCube cube,puz;
	/**A konkavitast garantalo darab forgatasai.*/
	private MatrixCube[] pieces;
	/**A megszerkesztett vagatok.*/
	private MatrixCube[] finalPieces;
	/**A vagatok szama.*/
	private int pieceNum;
	/**Kocka merete.*/
	private int size;
	
	/**
	 * A kocka meretevel inicializalo konstruktor.
	 * @param dim - kocka merete
	 */
	public CubeSlicer(int dim){
		size=dim;
		pieceNum=2*size;
		cube = new MatrixCube(size);
		cube.init();
		puz = new MatrixCube(size);
		puz.init();
		//megvaltoztatom a kockaba irhato ertekeket ... vagatok megkulonboztetese miatt
		//cube.setBound(pieceNum+1);
		//vagatreszletek elokeszitese
		kPiacePreparation();
		//vegso vagatok elokeszitese
		piacePreparation();
	}
	
	/**
	 * Elokeszit egy vektor a kocka vagatainak tarolasara.
	 */
	private void piacePreparation() {
		finalPieces = new MatrixCube[pieceNum];
		for(int i=0; i<pieceNum; i++){
			finalPieces[i] = new MatrixCube(size);
			finalPieces[i].init();
		}
	}

	/**
	 * A konkavitashoz szukseges darabok forgatasainak elokeszitese.
	 */
	private void kPiacePreparation(){
		MatrixCube piece = new MatrixCube(size);
		piece.init();
		//konkavitast garantalo darab bealliasa
		piece.setCubeElement(1, 1, 1, 1);
		piece.setCubeElement(1, 1, 2, 1);
		piece.setCubeElement(1, 1, 1, 2);
		//ennek a darabnak az osszes lehetseges forgatasanak legeneralasa
		pieces = new MatrixCube[24];
		pieces = piece.listOfCubeRotations();
	}
	
	public void slice(){
		Random gen = new Random();
		//kivalasztok egy konkav vagatot es az megprobalom beilleszteni a kockaba
		for(int num=0; num<pieceNum; num++){
			while(!attempt(num,pieces[gen.nextInt(24)],gen)){}
		}
		fill();
	}
	/**
	 * A parameterben megjeleno kockareszt megprobaljuk beilleszteni a kocka bizonyos szintjere.
	 * @param id	- hanyadik db. kockareszlet amit beillesztunk
	 * @param part	- maga a kockareszlet
	 * @param gen	- egy random generator ... kesobb kell
	 * @return	true, ha a beillesztes sikeres volt, kulonben false.
	 */
	private boolean attempt(int id, MatrixCube part, Random gen){
		//magassag beallitasa
		//a darabot felteszitjuk a tetejeb
		System.out.println("Kivalasztott darab:");
		part.getCube().printMatrix();
		System.out.println("------");
		System.out.println("Feltolas");
		while(part.shiftCubeUp2()){System.out.println("------");}
		System.out.println("------");
		//annyit nyomjuk le, hogy minden szinten 2 darabka legyen
		int num=(int)id/2;
		System.out.println("Letolas "+num+" szintet");
		while(num!=0&&part.shiftCubeDown2()){System.out.println("------");}
		System.out.println("------");
		//akkor megyunk balra, ha generalt szam = 0
		//boolean bal=(id%2==0);
		//if(bal){
			//elmegyunk teljesen balra
			System.out.println("Balra megyunk...");
			while(part.shiftCubeLeft2()){System.out.println("------");}
			System.out.println("------");
			//bejarjuk az adott szintet
			return walk(part,id);
		//}
		//else{//ha jobbra megyunk
		//	System.out.println("Jobbra megyunk...");
		//	while(part.shiftCubeRight2()){System.out.println("------");}
		//	System.out.println("------");
		//	//bejarjuk az adott szintet
		//	return walk(part,id);
		//}
	}
	/**
	 * Bejar egy szintet a kockabol, helyet keresve az adott kockareszenek.
	 * @param part - kockaresz
	 * @return	true, ha kapott egy helyet (akkor be is illeszti), kulonben false.
	 */
	private boolean walk(MatrixCube part, int id){
		//teljesen a kocka vegebe rakjuk
		System.out.println("Elore megyunk...");
		while(part.shiftCubeForward2()){System.out.println("------");}
		System.out.println("------");
		boolean back=true;
		//majd onnantol kezdve ameddig meg lehet jobbi jobbra, hol elore menve,
		//hol hatra menve megnezzuk a lehetosegeinket.
		do{
			while(back){
				if(cube.canAddCubes(part))
					return modSum(part,id);
				else
					if(!part.shiftCubeBack2())
						back=false;
			}
			while(!back){
				if(cube.canAddCubes(part))
					return modSum(part,id);
				else
					if(!part.shiftCubeForward2())
						back=true;
			}
			
		}while(part.shiftCubeRight2());
		return false;
	}
		
	public MatrixCube getPuz() {
		return puz;
	}

	public MatrixCube getCube() {
		return cube;
	}
	
	private boolean modSum(MatrixCube part, int id){
		for(int i=0; i<part.getSize(); i++)
			for(int j=0; j<part.getSize(); j++)
				for(int k=0; k<part.getSize(); k++)
					if(part.getCube().getElement(i, j, k)==1){
						cube.setCubeElement(1, i, j, k);
						puz.setCubeElement(id+2, i, j, k);
					}
		System.out.println("------");
		System.out.println("Puz");
		puz.getCube().printMatrix();
		System.out.println("Cube");
		cube.getCube().printMatrix();
		System.out.println("Part");
		part.getCube().printMatrix();
		System.out.println("------");
		return true;
	}
	
	private void fill(){
		int step=puz.getCube().emptySpace();
		out:
		while(step!=0){
			
			for (int i = 0; i < size; ++i)
				for (int j = 0; j < size; ++j)
					for (int k = 0; k < size; ++k)
						if(puz.getCubeElement(i,j,k)==0){
							puz.setCubeElement(neighbour(i,j,k), i, j, k);
							System.out.println("------");
							puz.getCube().printMatrix();
							if(puz.getCubeElement(i, j, k)!=0){
								step--;
								if(step==0)
									break out;
							}
						}
			
		}
	}
	
	public int neighbour(int i, int j, int k){
		int[] nh= new int[6];
		if(((i+1)>=0)&&((i+1)<puz.getSize()))
			nh[0]=puz.getCubeElement(i+1,j,k);
		else
			nh[0]=Integer.MAX_VALUE;
		if(((j+1)>=0)&&((j+1)<puz.getSize()))
			nh[1]=puz.getCubeElement(i,j+1,k);
		else
			nh[1]=Integer.MAX_VALUE;
		if(((k+1)>=0)&&((k+1)<puz.getSize()))
			nh[2]=puz.getCubeElement(i,j,k+1);
		else
			nh[2]=Integer.MAX_VALUE;
		if(((i-1)>=0)&&((i-1)<puz.getSize()))
			nh[3]=puz.getCubeElement(i-1,j,k);
		else
			nh[3]=Integer.MAX_VALUE;
		if(((j-1)>=0)&&((j-1)<puz.getSize()))
			nh[4]=puz.getCubeElement(i,j-1,k);
		else
			nh[4]=Integer.MAX_VALUE;
		if(((k-1)>=0)&&((k-1)<puz.getSize()))
			nh[5]=puz.getCubeElement(i,j,k-1);
		else
			nh[5]=Integer.MAX_VALUE;
		
		
		Random gen= new Random();
		int luckyNum;
		do{
			luckyNum=gen.nextInt(6);
		}
		while((nh[luckyNum]==Integer.MAX_VALUE)||(nh[luckyNum]==0));
			return nh[luckyNum];
	}
	
	
	public static void main (String[] args){
		CubeSlicer slicer =new CubeSlicer(3);
		slicer.slice();
		System.out.println("------");
		System.out.println("Puz");
		slicer.getPuz().printCube();
	}
}
